package math;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Random;

/**
 * 包含java.math包下三个类，BigDecimal,BigInteger,MathContext，一个枚举，RoundingMode
 *
 * @author wenyuan
 * @version 1.0
 * @date 2023/7/10
 * @since 8
 */
public class Math {

    public static void main(String[] args) {
        /* BigDecimal decimal = BigDecimal.ONE;
        int max_ceiling = BigDecimal.ROUND_CEILING;
        int round_down = BigDecimal.ROUND_DOWN;
        System.out.println(decimal);
        System.out.println(max_ceiling);
        System.out.println(round_down); */
        // whatBigDecimal();        // 1. 怎么构造 BigDecimal
        // aboutParameter();        // 2. 关于 Decimal 的参数
        // basicOperate();     // 3. 基础操作
        // useAdvancedly();        // 4. 高级应用
        useBigInteger();        // 5. BigInteger类
    }

    /**
     * 问题： 不推荐使用Double构造，因为第一手Double存储的值就已经精度缺失，会发生难以想象的问题，例如不管精度设置多少，10.0000输出都为0
     * 答案： 推荐使用String构造器
     */
    private static void whatBigDecimal() {
        // BigDecimal decimal = new BigDecimal(10.0000, new MathContext(4));    // out: 10, 不管精度设置为多少
        BigDecimal decimal = new BigDecimal("10.0000", new MathContext(4));
        System.out.println(decimal);
    }

    /**
     * 正如你看到的，BigDecimal类是一个不可变类，
     * 其提供的构造方法也是受限，因为原本应该有四个字段，它却只提供最多三个的外部可自定义值的字段，也就是不包括scale
     * 所以要设置scale，也就是比例其实也就是小数点。只有一种方法：
     * 第一种使用BigDecimal提供的静态构造类，这包含了四个参数。
     * 另外其precision字段是用来操作和计算的位数，但注意用0打头的是无效的
     */
    private static void aboutParameter() {
        BigDecimal decimal1 = new BigDecimal("5.55", new MathContext(2));
        BigDecimal decimal2 = new BigDecimal("-5.55", new MathContext(3));
        System.out.println("原始值：" + decimal2);
        BigDecimal abs = decimal2.abs(new MathContext(5));
        System.out.println("取绝对值：" + abs);
        BigDecimal add = decimal1.add(decimal2);
        BigDecimal decimal3 = add.setScale(5);
        System.out.println("加数：" + decimal1 + " 被加数：" + decimal2 + " 结果：" + decimal3);
        BigDecimal decimal4 = new BigDecimal("0.01", new MathContext(1));
        System.out.println("以0打头的设置精度无效：" + decimal4);
        BigDecimal decimal5 = new BigDecimal("1.12", new MathContext(1));
        System.out.println("不0打头的设置精度无效：" + decimal5);
    }

    /**
     * 每一个方法都也返回的BigDecimal对象也可以设置计算精度
     */
    private static void basicOperate() {
        BigDecimal decimal1 = new BigDecimal("3.11");
        BigDecimal decimal2 = new BigDecimal("11.4");
        BigDecimal addResult = decimal1.add(decimal2, new MathContext(3));
        // BigDecimal addResult = decimal1.add(decimal2);
        int compare = decimal1.compareTo(decimal2);
        String str = "";
        switch (compare) {
            case 0:
                str = "等于";
                break;
            case 1:
                str = "大于";
                break;
            case -1:
                str = "小于";
                break;
        }
        System.out.println("第一个数" + str + "第二个数");
        System.out.println("第一个数加第二个数等于：" + addResult);
        BigDecimal subtract = decimal1.subtract(decimal2);
        System.out.println("第一个数减第二个数等于：" + subtract);
        BigDecimal multiply = decimal1.multiply(decimal2);
        System.out.println("第一个数乘第二个数等于：" + multiply);
        BigDecimal divide = decimal2.divide(decimal1, 2, RoundingMode.HALF_UP);
        System.out.println("第二个数除第一个数等于：" + divide);
    }

    /**
     * 高级使用： 移动小数点 返回精度值 取余 返回对象 返回不带指数表示的字符串 返回正负号数字 丢掉末尾的0
     */
    private static void useAdvancedly() {
        BigDecimal decimal = new BigDecimal("781.414");

        BigDecimal decimal1 = decimal.movePointLeft(1);     // 78.1414
        System.out.println(decimal1);
        BigDecimal decimal2 = decimal1.movePointRight(2);       // 7814.14
        System.out.println(decimal2);

        int precision = decimal.precision();        // 6
        System.out.println(precision);

        BigDecimal remainder = decimal.remainder(new BigDecimal(2));// 取余
        System.out.println(remainder);

        System.out.println(decimal.plus());     // 只是简单的返回这个对象，没明白 781.414

        System.out.println(decimal.negate().toPlainString());        // 返回不带指数表示的字符串

        System.out.println(decimal.signum());       //  返回 0 -1 1

        System.out.println(decimal.stripTrailingZeros());       // 丢掉末尾的0
    }

    /**
     * BigInteger 是什么 有什么 怎么用
     */
    private static void useBigInteger() {
        int i = 10;
        BigInteger bigInteger;

        // 1. 根据比特位，及提供的随即种子构造一个 BigInteger
        System.out.println("1. 根据比特位，及提供的随即种子构造一个 BigInteger");
        do {
            bigInteger = new BigInteger(10, new Random(System.currentTimeMillis()));
            try {
                Thread.sleep(20);       // 睡眠20毫秒，不然好多数字都是一样的
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.print(bigInteger + "  ");
        } while ((i-- > 0));

        System.out.println();

        // 2. 将字符串转为 BigInteger
        System.out.println("2. 将字符串转为 BigInteger");
        bigInteger = new BigInteger("100");
        System.out.println(bigInteger);

        System.out.println();

        // 3. 根据基数翻译字符串，破案了，基数原来就是进制
        // radix:3 -> bigInteger:18 radix:8 -> bigInteger:
        // 10010 100000 110010 1000
        System.out.println("3. 根据基数翻译字符串，破案了，基数原来就是进制");
        bigInteger = new BigInteger("800", 10);
        System.out.println(bigInteger);

        // 4. 将byte数组位数拼接为一个整数就是这样做的，并且这是无限制大小的
        System.out.println("使用byte数组构造");
        bigInteger = new BigInteger(1, new byte[]{127, 127,127,127,127,127,127,127,127,127,127});
        System.out.println(bigInteger);

        // 5. 如果需要返回素数，则推荐使用方法  probablePrime ，而不是该构造方法，因为第二个参数代表的是返回素数的确定性，计算方法为 1 - 1/2^certainty
        System.out.println("5.返回素数或不确定返回素数");
        for (int j = 0; j < 100; j++) {
            bigInteger = new BigInteger(4, 0, new Random(System.currentTimeMillis()));
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                System.out.println(e);
            }
            System.out.println(bigInteger);
        }

    }
}
